#include <math.h>
#include <time.h>
#include <windows.h>
#include "pThreads/semaphore.h"
#include <vector>
#include "CSemaphore.h"
#include <algorithm>
#include "MySQL/mysql.h"

#include "CNpc.h"
#include "CUtil.h"
#include "CConsole.h"
#include "CGlobal.h"
#include "CDb.h"
#include "CAbility.h"
#include "CActionPacket.h"
#include "CMessagePacket.h"
#include "CLuaManager.h"
#include "CCharStatsPacket.h"
#include "CCharSkillPacket.h"

// loads an ability from the db
CAbility::CAbility(unsigned short abilityID) {
	char query[8192];
	unsigned long count;
	MYSQL_RES * res;
	MYSQL_ROW row;
	memset(query,0,8000);

	sprintf(query,"SELECT abilityId,ability,job,level,animationId,category,validTarget,recast,isAOE,`range`,recastID FROM abilities WHERE abilityId = %i",abilityID-16);
	count = (int) strlen(query);

	MYSQL* mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);

	res = mysql_store_result(mysqlConn);
	CDb::pushConn(mysqlConn); //release conn
	if(!res)
	{
		CConsole::outErr("Error looking up Ability!");
	} else if(!mysql_num_rows(res)){
		mysql_free_result(res);
		CConsole::outErr("Error looking up Ability!");	
	} else {
		row = mysql_fetch_row(res);
		this->m_abilityID		= atoi(row[0]);
		memset(this->m_abilityName,0,50);
		memcpy(this->m_abilityName,row[1],50);
		this->m_job				= atoi(row[2]);
		this->m_level			= atoi(row[3]);
		this->m_animID			= atoi(row[4]);
		this->m_animCatID		= atoi(row[5]);
		this->m_allowedTargets	= atoi(row[6]);
		this->m_recastTime		= atoi(row[7]);
		this->m_isAOE			= atoi(row[8]);
		this->m_range			= atoi(row[9]);
		this->m_recastID		= atoi(row[10]);
		this->m_message			= 0x64;
		this->m_result			= 0;
		
		mysql_free_result(res);
	}

	
}

CAbility::~CAbility(void)
{
}

char * CAbility::getAbilityName() {
	return this->m_abilityName;
}

unsigned short CAbility::getAbilityID() {
	return this->m_abilityID;
}

unsigned short CAbility::getAbilityAnimID() {
	return this->m_animID;
}

unsigned short CAbility::getAbilityAnimCatID(){ //TODO: does this need to be unsigned short? In packet it is unsigned char!
	return this->m_animCatID;
}

void CAbility::setAbilityTargetID(unsigned int targetID) {
	this->m_targetID = targetID;
}

void CAbility::setUser(CBaseEntity * pUser) {
	this->m_pUser = pUser;
}

void CAbility::setMainTarget(CBaseEntity * pTarget) {
	this->m_pMainTarget = pTarget;
}

unsigned int CAbility::getAbilityTargetID() {
	return this->m_targetID;
}

CBaseEntity * CAbility::getUser() {
	return this->m_pUser;
}

CBaseEntity * CAbility::getMainTarget() {
	return this->m_pMainTarget;
}

unsigned short CAbility::getAbilityRange() {
	return this->m_range;
}

unsigned short CAbility::getAbilityRecast() {
	return this->m_recastTime;
}

unsigned short CAbility::getAbilityRecastID() {
	return this->m_recastID;
}

void CAbility::performAbility() {

	// build final targetlist
	if( this->isAOEAbility() ) {
		// if spell has AOE effect, push those in range onto the targetlist
		if(this->getMainTarget()->isPlayer()) {
			std::vector<CChar*> tmpList;
			tmpList = ((CChar*)this->getUser())->getPartyMembersInRange(this->getMainTarget()->getPos(), 15);
			this->addTarget(this->getMainTarget());
			for(unsigned int i = 0; i < tmpList.size(); i++) {
				if( !tmpList.at(i)->isDead() && (tmpList.at(i)->getId() != this->getMainTarget()->getId())) {
					this->addTarget(tmpList.at(i));
				}	
			}
		}
	} else {
		this->addTarget(this->getMainTarget());
	}

	CChar * schar = (CChar*)this->getUser();

	CActionPacket* actionPacket=new CActionPacket();
	actionPacket->setActorId(schar->getId());
	actionPacket->setCategorie(this->getAbilityAnimCatID()); 

	actionPacket->setParameter1(this->getAbilityID()+16);

	apActionEntry newEntry;
	memset(&newEntry,0,sizeof(newEntry));


	
	for(unsigned int i = 0; i < this->m_targetList.size(); i++) {
	
		CLuaManager::OnUseAbility(schar, this->m_targetList.at(i), this, (i == 0 ? true : false ));
	
		memset(&newEntry,0,sizeof(apActionEntry));
		
		newEntry.unknownValue		=	0x10;
		newEntry.targetAnimationID	=	this->getAbilityAnimID();

		newEntry.parameter2			=	this->getResult();
		newEntry.messageType		=	this->getMessage();


		actionPacket->setTargetID(i+1,this->m_targetList.at(i)->getId());
		actionPacket->setActionEntry(i+1,1,&newEntry);

		
	
	}


	actionPacket->finalizePacket();
	schar->insertPacket(CHAR_INRANGE_SELF, *actionPacket);

	struct abilityRecast ac;
	ac.abilityID	= this->getAbilityID();
	ac.timeStamp	= (int)time(NULL);
	ac.recast		= this->getAbilityRecast();
	ac.recastID		= this->m_recastID;

	((CChar*)this->getUser())->lockRecastAbilityList();
	((CChar*)this->getUser())->recastAbilityList.push_back(ac);
	((CChar*)this->getUser())->unlockRecastAbilityList();

	((CChar*)this->getUser())->quePacket(CCharStatsPacket(((CChar*)this->getUser())));
	((CChar*)this->getUser())->quePacket(CCharSkillPacket(((CChar*)this->getUser())));

}

// Return a bitmask with the allowed target-types
unsigned short CAbility::getAllowedTargets() {
	return this->m_allowedTargets;
}


// returns true if the target is valid to the ability
bool CAbility::isValidTarget(CBaseEntity * pTarget) {

	CChar * schar = (CChar*)this->getUser();

	if(pTarget->getObjectType() == OBJ_NPC) {
		return false;
	}

	if(!(this->getAllowedTargets() & TARGET_SELF)) {
		if(this->getUser()->getId() == this->getMainTarget()->getId()) {
			return false;
		}
	}

	if(!(this->getAllowedTargets() & TARGET_MOB)) {
		if((this->getMainTarget()->isMob())) {
			return false;
		}
	}
			
	if(!(this->getAllowedTargets() & TARGET_PLAYER)) {

		if(this->getMainTarget()->isPlayer()) {

			if((this->getAllowedTargets() & TARGET_PLAYER_DEAD) && this->getMainTarget()->isDead()) {
				return true;
			} else if((this->getAllowedTargets() & TARGET_PLAYER_PARTY)) {
				if( ((CChar*)this->getUser())->isPartyMember(((CChar*)this->getMainTarget()))) {
					return true;
				}
			} else if(this->getUser()->getId() == this->getMainTarget()->getId()) {
				if(!(this->getAllowedTargets() & TARGET_SELF)) {
					return false;
				}
			} else {
				return false;
			}
		}
	}

	if((this->getAllowedTargets() & TARGET_PLAYER)) {
		if(this->getMainTarget()->isDead() && !(this->getAllowedTargets() & TARGET_PLAYER_DEAD)) {
			return false;	
		}
	}

	return true;
}

bool CAbility::isAOEAbility() {

	return (this->m_isAOE == 1 ? true : false);

}

void CAbility::addTarget(CBaseEntity * pTarget) {

	this->m_targetList.push_back(pTarget);

}

void CAbility::setMessage(unsigned short msg) {
	this->m_message = msg;
}

unsigned short CAbility::getMessage() {
	return this->m_message;
}

void CAbility::setResult(unsigned short result) {
	this->m_result = result;
}

unsigned short CAbility::getResult() {
	return this->m_result;
}


void CAbility::setAnimation(unsigned short animation) {
    this->m_animID = animation;
}